package qc.module.qms.api.controller;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import qc.common.core.enums.qms.QmsDataFlowOperateTypeEnum;
import qc.common.core.enums.qms.QmsPointDataQualityEnum;
import qc.common.core.enums.qms.QmsPointTimeSegmentEnum;
import qc.common.core.exception.QCPromptException;
import qc.common.core.unify.QCUnifyReturnValue;
import qc.common.core.utils.LocalDateTimeUtil;
import qc.common.core.utils.QmsTimeUtil;
import qc.module.qms.algorithm.NumericCalc.dto.QmsNumericCalcPointRelation;
import qc.module.qms.algorithm.NumericCalc.dto.QmsNumericCalcUseMethod;
import qc.module.qms.dto.calc.QmsPointDataCalcJudgeResultDto;
import qc.module.qms.dto.calc.QmsPointDataCalcResultDto;
import qc.module.qms.dto.calc.QmsPointDataCalcSaveResultDto;
import qc.module.qms.dto.debug.*;
import qc.module.qms.dto.pointdata.QmsPointDataManageResultDto;
import qc.module.qms.dto.pointdata.QmsPointNumericDataDto;
import qc.module.qms.dto.pointdata.QmsPointNumericSaveDataDto;
import qc.module.qms.entity.QmsPoint;
import qc.module.qms.service.QmsBasicConfigCacheGetService;
import qc.module.qms.service.calc.QmsNumericCalcGetService;
import qc.module.qms.service.calc.QmsNumericCalcPointRelationService;
import qc.module.qms.service.pointdata.QmsPointDataCalcService;
import qc.module.qms.service.pointdata.QmsPointDataManageService;
import qc.module.qms.service.pointdata.QmsPointDataQueryService;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 用于调试和测试controller
 * 用于调试和测试数据计算、数据触发关联计算等，可以直接调用所有Service中的方法
 *
 * @author QuCheng Tech
 * @since 2025/9/24
 */
@RestController
@RequestMapping("/debug")
public class DebugTestController {
    private static final Logger logger = LoggerFactory.getLogger(DebugTestController.class);

    @Autowired
    private QmsBasicConfigCacheGetService basicConfigCacheGetService;

    @Autowired
    private QmsNumericCalcGetService numericCalcGetService;

    @Autowired
    private QmsPointDataManageService pointDataManageService;

    @Autowired
    private QmsPointDataQueryService pointDataQueryService;

    @Autowired
    private QmsNumericCalcPointRelationService numericCalcPointRelationService;

    @Autowired
    private QmsPointDataCalcService pointDataCalcService;

    /**
     * 测试指定的操作代码，用于调试或测试接口或方法
     *
     * @param id 操作代码
     * @return java.lang.String
     * @author QuCheng Tech
     * @since 2025/9/30
     */
    @GetMapping("/test")
    public String testByParam(@RequestParam String id) {
        return test(id);
    }

    /**
     * 测试指定的操作代码，用于调试或测试接口或方法
     *
     * @param id 操作代码
     * @return java.lang.String
     * @author QuCheng Tech
     * @since 2025/9/30
     */
    @GetMapping("/test/{id}")
    public String testByPath(@PathVariable String id) {
        return test(id);
    }

    /**
     * 测试指定的操作代码，用于调试或测试接口或方法
     *
     * @param opcode 操作代码
     * @return java.lang.String
     * @author QuCheng Tech
     * @since 2025/9/30
     */
    String test(String opcode) {
        if (StringUtils.equalsIgnoreCase(opcode, "judgeQuality")) {
            //测试点号数据质量对比判断
            testJudgeQuality();
        } else if (StringUtils.equalsIgnoreCase(opcode, "pointRelation")) {
            //测试输出所有点号与触发计算点号关联关系
            testPointRelation();
        }

        return QCUnifyReturnValue.Success();
    }

    //测试点号数据质量对比判断
    void testJudgeQuality() {
        for (QmsPointDataQualityEnum quality : QmsPointDataQualityEnum.values()) {
            for (QmsPointDataQualityEnum existQuality : QmsPointDataQualityEnum.values()) {
                boolean judgeResult = QmsPointDataQualityEnum.judgeNeedUpdateData(quality, existQuality);
                logger.debug("本次数据质量[" + quality + "]，已有数据质量[" + existQuality + "]，是否需要更新数据判断结果[" + judgeResult + "]");
            }
        }
    }

    //测试输出所有点号与触发计算点号关联关系
    void testPointRelation() {
        List<QmsNumericCalcPointRelation> allRelations = numericCalcPointRelationService.getAllWithCache();
        if (allRelations == null || allRelations.size() < 0x1) {
            logger.debug("无任何点号触发关联关系。");
        } else {
            logger.debug("点号触发关联关系共[" + allRelations.size() + "]条");
            logger.debug("点号ID\t点号时段类型\t关联的输入点号ID\t关联的输入点号时段类型\t查询点号数据时间类型\t起始时间偏移\t不包含起始时间点\t截止时间偏移\t不包含截止时间点\t需要起始时间最近一条数据\t往前时段数");
            for (QmsNumericCalcPointRelation relation : allRelations) {
                logger.debug(relation.getPointid() + "\t" + relation.getTmseg() + "\t" + relation.getInputPointid() + "\t" + relation.getInputTmseg()
                        + "\t" + relation.getTmrange() + "\t" + relation.getTmbeginoffset() + "\t" + relation.getExcludebegintm()
                        + "\t" + relation.getTmendoffset() + "\t" + relation.getExcludeendtm() + "\t" + relation.getNeedbegintmlatest() + "\t" + relation.getBegintmlatestoffset());
            }
        }
    }

    /**
     * 点号数值计算，传入要计算的点号、时段类型和起止时段，返回计算结果
     * 用于测试数值计算方法，包含完整的输入参数和点号数据提取、数值计算方法调用和结果
     *
     * @param dto 要计算的点号、时段类型和起止时段
     * @return qc.module.qms.algorithm.NumericCalc.dto.QmsNumericCalcResult
     * @author QuCheng Tech
     * @since 2025/9/26
     */
    @RequestMapping(value = "/pointcalc", method = {RequestMethod.POST, RequestMethod.GET})
    public QmsPointDataCalcResultDto pointNumericCalc(@RequestBody DebugQmsNumericCalcDestDto dto) throws QCPromptException {
        //判断不能为空的属性
        if (dto == null)
            throw new QCPromptException("输入的对象不能为空");
        if (StringUtils.isBlank(dto.getPointid()))
            throw new QCPromptException("输入的对象中点号不能为空");
        if (StringUtils.isBlank(dto.getBeginTime()))
            throw new QCPromptException("输入的对象中起始时间不能为空");
        if (StringUtils.isBlank(dto.getEndTime()))
            throw new QCPromptException("输入的对象中截止时间不能为空");
        //转换输入的数据
        QmsPointTimeSegmentEnum tmseg = QmsPointTimeSegmentEnum.getQmsPointTimeSegmentEnum(dto.getTmseg());
        if (tmseg == QmsPointTimeSegmentEnum.UN_DEFINE)
            throw new QCPromptException("输入的对象中时段类型错误");
        LocalDateTime beginTime = LocalDateTimeUtil.parse(dto.getBeginTime());
        LocalDateTime endTime = LocalDateTimeUtil.parse(dto.getEndTime());
        if (beginTime == null)
            throw new QCPromptException("输入的对象中起始时间不能转换为有效的时间");
        if (endTime == null)
            throw new QCPromptException("输入的对象中截止时间不能转换为有效的时间");
        if (beginTime.isAfter(endTime))
            throw new QCPromptException("输入的起始时间不能大于截止时间");

        //设置optype为未定义仅进行计算获取计算结果，不对计算出的点号数据进行保存
        QmsDataFlowOperateTypeEnum optype = QmsDataFlowOperateTypeEnum.UN_DEFINE;
        //设置optype为人工进行点号重新计算，自动对计算出的点号数据进行保存
        //QmsDataFlowOperateTypeEnum optype = QmsDataFlowOperateTypeEnum.CALC_MANUAL;

        //根据点号ID获取点号对象
        QmsPoint point = basicConfigCacheGetService.getPoint(dto.getPointid());
        if (point == null)
            throw new QCPromptException("输入的对象中点号ID错误，根据点号ID获取不到对应点号对象");

        //获取点号的计算方法，也可以从外部传入指定的计算方法
        QmsNumericCalcUseMethod calcMethod = numericCalcGetService.getPointValidCalcUseMethod(point, tmseg);
        if (calcMethod == null)
            throw new QCPromptException("无有效的计算方法，无法进行数值计算");

        QmsPointDataCalcSaveResultDto calcSaveResultDto = pointDataCalcService.calcPointDatas(point, tmseg, calcMethod,
                beginTime, endTime, optype, true, null, null);

        if (calcSaveResultDto == null)
            throw new QCPromptException("计算失败，方法返回的对象为null");
        if (calcSaveResultDto.getCalcResult() == null)
            throw new QCPromptException("计算失败，计算结果对象为null");

        QmsPointDataCalcResultDto calcResult = calcSaveResultDto.getCalcResult();
        if (calcResult.isSuccess() == false) {
            logger.debug("计算失败，" + calcResult.getMsg());
        } else {
            //2025-9-27，实测测试在此输出结果顺序是按时间Asc，在Apifox中的顺序与此处显示不同
            if (calcResult.getDatas() != null && calcResult.getDatas().size() > 0x0) {
                logger.debug("计算结果对象--result--计算结果集合数量：共" + calcResult.getDatas().size() + "个");
                logger.debug("时间\t数值");
                for (Map.Entry<LocalDateTime, Double> entry : calcResult.getDatas().entrySet()) {
                    logger.debug(QmsTimeUtil.format(entry.getKey(), tmseg) + "\t" + entry.getValue());
                }
            } else {
                logger.debug("计算结果对象--result--计算结果集合数量：无");
            }
        }

        return calcResult;
    }

    /**
     * 获取指定点号和时段类型触发关联关系集合
     *
     * @param dto 触发源点号和时段类型
     * @return java.util.List<qc.module.qms.algorithm.NumericCalc.dto.QmsNumericCalcPointRelation>
     * @author QuCheng Tech
     * @since 2025/9/30
     */
    @RequestMapping(value = "/pointtrigger", method = {RequestMethod.POST, RequestMethod.GET})
    public List<QmsNumericCalcPointRelation> getPointTriggerRelations(@RequestBody DebugQmsPointTriggerSourceDto dto) throws QCPromptException {
        //判断不能为空的属性
        if (dto == null)
            throw new QCPromptException("输入的对象不能为空");
        if (StringUtils.isBlank(dto.getPointid()))
            throw new QCPromptException("输入的对象中点号不能为空");
        //转换输入的数据
        QmsPointTimeSegmentEnum tmseg = QmsPointTimeSegmentEnum.getQmsPointTimeSegmentEnum(dto.getTmseg());
        if (tmseg == QmsPointTimeSegmentEnum.UN_DEFINE)
            throw new QCPromptException("输入的对象中时段类型错误");

        return numericCalcGetService.getPointTriggerRelations(dto.getPointid(), tmseg);
    }

    /**
     * 保存点号数值数据
     *
     * @param dto 点号数值数据对象
     * @return qc.module.qms.dto.pointdata.QmsPointDataManageResultDto
     * @author QuCheng Tech
     * @since 2025/10/2
     */
    @RequestMapping(value = "/savenumericdata", method = {RequestMethod.POST, RequestMethod.GET})
    public QmsPointDataManageResultDto savePointNumericData(@RequestBody DebugQmsPointNumericDataSaveDto dto) throws QCPromptException {
        if (dto == null)
            throw new QCPromptException("保存的对象不能为空");
        if (StringUtils.isBlank(dto.getPointid()))
            throw new QCPromptException("保存的对象中点号不能为空");
        if (StringUtils.isBlank(dto.getTm()))
            throw new QCPromptException("保存的对象中数据时标不能为空");
        LocalDateTime time = LocalDateTimeUtil.parse(dto.getTm());
        if (time == null)
            throw new QCPromptException("保存的对象中数据时标无法转换为有效的时间");
        QmsPointTimeSegmentEnum tmseg = QmsPointTimeSegmentEnum.getQmsPointTimeSegmentEnum(dto.getTmseg());
        if (tmseg == QmsPointTimeSegmentEnum.UN_DEFINE)
            throw new QCPromptException("保存的对象中时段类型错误");
        //数据值可以为空，不为空时需要能转换为有效的数值
        Double v = null;
        if (StringUtils.isNotBlank(dto.getV())) {
            double dv = Double.parseDouble(dto.getV());
            if (Double.isNaN(dv) == true)
                throw new QCPromptException("保存的对象中数据值无效");
            v = dv;
        }
        QmsPointDataQualityEnum quality = QmsPointDataQualityEnum.getQmsPointDataQualityEnum(dto.getQuality());

        //将传入的dto转换为QmsPointNumericSaveDataDto
        QmsPointNumericSaveDataDto saveData = new QmsPointNumericSaveDataDto();
        saveData.setTm(time);
        saveData.setV(v);
        saveData.setQuality(quality);

        List<QmsPointNumericSaveDataDto> datas = new ArrayList<>();
        datas.add(saveData);

        //测试使用，根据传入的是否为人工操作分别调用不同的方法
        if (dto.isManual() == true)
            return pointDataManageService.saveManualEntryNumericDatas(dto.getPointid(), tmseg, datas, "测试用户");
        else
            //return pointDataManageService.saveManualCalcDatas(dto.getPointid(), tmseg, datas,QmsDataFlowOperateTypeEnum.CALC_MANUAL, null, null, "测试用户");
            return pointDataManageService.saveSystemCalcNumericDatas(dto.getPointid(), tmseg, datas, QmsDataFlowOperateTypeEnum.CALC_POINT_DATA_BY_TIMER, null, null);
    }

    /**
     * 查询指定时间段的点号数据集合
     *
     * @param condition 查询条件
     * @return java.util.List<qc.module.qms.dto.pointdata.QmsPointNumericDataDto>
     * @author QuCheng Tech
     * @since 2025/10/2
     */
    @RequestMapping(value = "/querypointdata", method = {RequestMethod.POST, RequestMethod.GET})
    public List<QmsPointNumericDataDto> queryPointDatas(@RequestBody DebugQmsPointNumericDataQueryConditionDto condition) throws QCPromptException {
        if (condition == null)
            throw new QCPromptException("查询条件对象不能为空");
        if (StringUtils.isBlank(condition.getPointid()))
            throw new QCPromptException("查询条件对象中点号不能为空");
        if (StringUtils.isBlank(condition.getBeginTime()))
            throw new QCPromptException("查询条件中起始时间不能为空");
        LocalDateTime beginTime = LocalDateTimeUtil.parse(condition.getBeginTime());
        if (beginTime == null)
            throw new QCPromptException("查询条件中起始时间无法转换为有效的时间");
        if (StringUtils.isBlank(condition.getEndTime()))
            throw new QCPromptException("查询条件中截止时间不能为空");
        LocalDateTime endTime = LocalDateTimeUtil.parse(condition.getEndTime());
        if (endTime == null)
            throw new QCPromptException("查询条件中截止时间无法转换为有效的时间");
        QmsPointTimeSegmentEnum tmseg = QmsPointTimeSegmentEnum.getQmsPointTimeSegmentEnum(condition.getTmseg());
        if (tmseg == QmsPointTimeSegmentEnum.UN_DEFINE)
            throw new QCPromptException("查询条件中时段类型错误");
        QmsPointDataQualityEnum quality = QmsPointDataQualityEnum.getQmsPointDataQualityEnum(condition.getQuality());

        return pointDataQueryService.query(condition.getPointid(), tmseg, beginTime, endTime, condition.isExcludeBeginTime(), condition.isExcludeEndTime(), quality);
    }

    /**
     * 获取指定时间点的点号数据
     *
     * @param condition 获取条件
     * @return qc.module.qms.dto.pointdata.QmsPointNumericDataDto
     * @author QuCheng Tech
     * @since 2025/10/2
     */
    @RequestMapping(value = "/getpointdata", method = {RequestMethod.POST, RequestMethod.GET})
    public QmsPointNumericDataDto getPointData(@RequestBody DebugQmsPointNumericDataGetConditionDto condition) throws QCPromptException {
        if (condition == null)
            throw new QCPromptException("查询条件对象不能为空");
        if (StringUtils.isBlank(condition.getPointid()))
            throw new QCPromptException("查询条件对象中点号不能为空");
        if (StringUtils.isBlank(condition.getTime()))
            throw new QCPromptException("查询条件中数据时标不能为空");
        LocalDateTime time = LocalDateTimeUtil.parse(condition.getTime());
        if (time == null)
            throw new QCPromptException("查询条件中数据时标无法转换为有效的时间");
        QmsPointTimeSegmentEnum tmseg = QmsPointTimeSegmentEnum.getQmsPointTimeSegmentEnum(condition.getTmseg());
        if (tmseg == QmsPointTimeSegmentEnum.UN_DEFINE)
            throw new QCPromptException("查询条件中时段类型错误");
        QmsPointDataQualityEnum quality = QmsPointDataQualityEnum.getQmsPointDataQualityEnum(condition.getQuality());

        return pointDataQueryService.get(condition.getPointid(), tmseg, time, quality);
    }

    /**
     * 定时点号计算时段判断
     *
     * @param condition 输入条件
     * @return qc.module.qms.dto.calc.QmsPointDataCalcJobJudgeResultDto
     * @author QuCheng Tech
     * @since 2025/10/9
     */
    @RequestMapping(value = "/calcjudge", method = {RequestMethod.POST, RequestMethod.GET})
    public QmsPointDataCalcJudgeResultDto getPointCalcJobJudge(@RequestBody DebugQmsPointDataCalcJobJudgeConditionDto condition) throws QCPromptException {
        //判断不能为空的属性
        if (condition == null)
            throw new QCPromptException("输入的对象不能为空");
        if (StringUtils.isBlank(condition.getPointid()))
            throw new QCPromptException("输入的对象中点号不能为空");
        //转换输入的数据
        QmsPointTimeSegmentEnum tmseg = QmsPointTimeSegmentEnum.getQmsPointTimeSegmentEnum(condition.getTmseg());
        if (tmseg == QmsPointTimeSegmentEnum.UN_DEFINE)
            throw new QCPromptException("输入的对象中时段类型错误");

        LocalDateTime time = null;
        if (StringUtils.isNotBlank(condition.getTime())) {
            time = LocalDateTimeUtil.parse(condition.getTime());
            if (time == null)
                throw new QCPromptException("输入的对象中时间不能转换为有效的时间");
        } else {
            time = LocalDateTimeUtil.getNow();
        }

        //根据点号ID获取点号对象
        QmsPoint point = basicConfigCacheGetService.getPoint(condition.getPointid());
        if (point == null)
            throw new QCPromptException("输入的对象中点号ID错误，根据点号ID获取不到对应点号对象");

        //获取点号的计算方法，也可以从外部传入指定的计算方法
        QmsNumericCalcUseMethod calcMethod = numericCalcGetService.getPointValidCalcUseMethod(point, tmseg);
        if (calcMethod == null)
            throw new QCPromptException("无有效的计算方法，无法进行数值计算");

        return numericCalcGetService.getTimerJobCalcPointDataJudgeResult(point, tmseg, calcMethod, time);
    }
}
